﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using BCCommon;
using BCCommon.Extensions;
using BCCommon.Http;
using BCCommon.PaymentEnums;
using BCCommon.ZipUtils;
using BCData.Common.Payment.AliPayBill;
using BCDto.Common.Payment.AliPayBill;
using BCDto.Common.Payment.AliPayBill.AliPayBillDetail;
using BCDto.Common.Payment.AliPayBill.AliPayBillStatistics;
using BCEntity.Common.Payment.AliPayBill;
using BCEntity.Common.Payment.AliPayBill.AliPayBillDetail;
using BCEntity.Common.Payment.AliPayBill.AliPayBillStatistics;
using BCService.Utils;
using Microsoft.Extensions.Configuration;

namespace BCService.Common.Payment.PaymentBill.AliPayBill
{
    public class AliPayBillService : IAliPayBillService
    {
        private readonly IConfiguration configuration;
        private readonly IAliPayBillData aliPayBillData;
        private static Dictionary<string, string> accountDic = new Dictionary<string, string>();

        public AliPayBillService(IConfiguration configuration,IAliPayBillData aliPayBillData)
        {
            this.configuration = configuration;
            this.aliPayBillData = aliPayBillData;
            if (accountDic.Count == 0)
            {
                InitAccountInfo();
            }
        }

        public AliPayBillDto Insert(DateTime billDate, AliPayBillType billType, string url, SourceAppType appType)
        {
            if (string.IsNullOrWhiteSpace(url))
            {
                return new AliPayBillDto();
            }
            if (this.aliPayBillData.SomeDayBillExists(billDate, billType,appType).GetAwaiter().GetResult())
            {
                return new AliPayBillDto();
            }
            AliPayBillEntity billEntity = this.DownloadAndSerializeBill(billDate, billType, url, appType);
            if (!this.aliPayBillData.Insert(billEntity).GetAwaiter().GetResult())
            {
                throw new ArgumentException("添加支付宝对账单数据失败");
            }

            AliPayBillDto billDto = billEntity.As<AliPayBillDto>();
            return billDto;
        }

        public PageableList<AliPayBillDetailDto, AliPayBillStatisticsDto> GetSomeDayBill(DateTime tradeDate, AliPayBillType aliPayBillType, int pageIndex, int pageSize)
        {
            var data = this.aliPayBillData.GetSomeDayBill(tradeDate, aliPayBillType).GetAwaiter().GetResult();
            var result = new PageableList<AliPayBillDetailDto, AliPayBillStatisticsDto>
            {
                Items = data.Item1.Skip((pageIndex - 1) * pageSize).Take(pageSize).As<List<AliPayBillDetailDto>>(),
                Count = data.Item1.Count(),
                PageIndex = pageIndex,
                PageSize = pageSize,
                Statistics = data.Item2.As<AliPayBillStatisticsDto>()
            };
            return result;
        }

        public PageableList<AliPayBillDetailDto, AliPayBillStatisticsDto> GetSomeMonthBill(int year, int month, AliPayBillType aliPayBillType, int pageIndex, int pageSize)
        {
            var data = this.aliPayBillData.GetSomeMonthBill(year, month, aliPayBillType).GetAwaiter().GetResult();
            var result = new PageableList<AliPayBillDetailDto, AliPayBillStatisticsDto>
            {
                Items = data.Item1.Skip((pageIndex - 1) * pageSize).Take(pageSize).As<List<AliPayBillDetailDto>>(),
                Count = data.Item1.Count(),
                PageIndex = pageIndex,
                PageSize = pageSize,
                Statistics = data.Item2.As<AliPayBillStatisticsDto>()
            };
            return result;
        }

        public PageableList<AliPayBillDetailDto, AliPayBillStatisticsDto> GetSomeYearBill(int year, AliPayBillType aliPayBillType, int pageIndex, int pageSize)
        {
            var data = this.aliPayBillData.GetSomeYearBill(year, aliPayBillType).GetAwaiter().GetResult();
            var result = new PageableList<AliPayBillDetailDto, AliPayBillStatisticsDto>
            {
                Items = data.Item1.Skip((pageIndex - 1) * pageSize).Take(pageSize).As<List<AliPayBillDetailDto>>(),
                Count = data.Item1.Count(),
                PageIndex = pageIndex,
                PageSize = pageSize,
                Statistics = data.Item2.As<AliPayBillStatisticsDto>()
            };
            return result;
        }
        #region 支付宝交易对账单下载及数据处理

        #region 下载并转换对账单数据
        /// <summary>
        /// 下载并转换对账单数据
        /// </summary>
        /// <param name="billDate">对账单日期（仅支持2016年1月1日开始的账单）</param>
        /// <param name="billType">对账单类型</param>
        /// <param name="url">支付宝对账单下载地址</param>
        /// <param name="appType">App类型</param>
        private AliPayBillEntity DownloadAndSerializeBill(DateTime billDate, AliPayBillType billType, string url, SourceAppType appType)
        {
            Dictionary<string, string> dic = this.DownloadBill(billDate, billType, url);
            string filePath = dic["filePath"];
            string fileType = dic["fileType"];
            string fileName = dic["downloadFileName"].Replace("." + fileType, "");
            string directory = filePath.Replace(dic["downloadFileName"], "");
            string targetDir = ZipUtils.UnZipByZipFileComponent(fileName, directory, fileType);
            string[] filePaths = Directory.GetFiles(targetDir);
            Dictionary<string, string> fileDic = new Dictionary<string, string>();
            for (int i = 0; i < filePaths.Length; i++)
            {
                byte[] data = FileSystem.File2Bytes(filePaths[i]);
                string value = Encoding.GetEncoding("gb2312").GetString(data);
                string[] lineString = value.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
                StringBuilder str = new StringBuilder();
                foreach (string line in lineString)
                {
                    if (line.Substring(0, 1) != "#")
                    {
                        str.AppendLine(line);
                    }
                }
                fileDic.Add(filePaths[i], str.ToString());
            }
            //FileSystem.DeleteDirAllFile(targetDir);

            List<AliPayBillDetailEntity> aliPayBillDetailEntities = new List<AliPayBillDetailEntity>();
            List<AliPayBillStatisticsEntity> aliPayBillStatisticsEntities = new List<AliPayBillStatisticsEntity>();

            if (fileDic.Any())
            {
                foreach (KeyValuePair<string, string> item in fileDic)
                {
                    if (item.Key.Contains("业务明细"))
                    {
                        if (item.Key.Contains("业务明细(汇总)"))
                        {
                            aliPayBillStatisticsEntities.AddRange(GetBillStatistics(item.Value, billDate, appType, billType));
                        }
                        else
                        {
                            aliPayBillDetailEntities.AddRange(GetBillDetailList(item.Value, appType, billType));
                        }
                    }
                }
            }

            AliPayBillEntity billEntity = new AliPayBillEntity
            {
                Details = aliPayBillDetailEntities,
                Statistics = aliPayBillStatisticsEntities
            };
            return billEntity;
        }
        #endregion

        #region 支付宝对账单明细数据提取
        /// <summary>
        /// 支付宝对账单明细数据提取
        /// </summary>
        /// <param name="billStr">账单数据</param>
        /// <param name="appType">应用类型</param>
        /// <param name="billType">账单类型</param>
        /// <returns></returns>
        private List<AliPayBillDetailEntity> GetBillDetailList(string billStr,SourceAppType appType, AliPayBillType billType)
        {
            billStr = billStr.Replace("\r", "");
            string[] data = billStr.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
            string[] billTitle = data[0].Split(',');

            List<AliPayBillDetailEntity> bills = new List<AliPayBillDetailEntity>();
            for (int i = 1; i < data.Length; i++)
            {
                string[] billResult = data[i].Split(',');
                AliPayBillDetailEntity bill = new AliPayBillDetailEntity();
                for (int j = 0; j < billTitle.Length; j++)
                {
                    string value = billResult[j].Trim();
                    switch (billTitle[j])
                    {
                        case "创建时间":
                            bill.TradeTime = DateTime.Parse(value);
                            bill.TradeDate = bill.TradeTime.Date;
                            break;
                        case "终端号":
                            bill.DeviceNo = string.IsNullOrWhiteSpace(value) ? null : value;
                            break;
                        case "支付宝交易号":
                            bill.AliPayOrderNo = value;
                            bill.AppId = accountDic[appType.ToString()];
                            bill.AppName = appType.ToString();
                            break;
                        case "商户订单号":
                            bill.PaymentRecordId = Convert.ToInt64(value);
                            bill.MchId = configuration["AliPay:ClientApp:APP_ID"];
                            break;
                        case "对方账户":
                            bill.AliUserNo = value;
                            break;
                        case "业务类型":
                            bill.BusinessType = billType.ToString();
                            bill.BusinessTypeStr = value;
                            break;
                        case "完成时间":
                            bill.CompleteTime = DateTime.Parse(value);
                            break;
                        case "门店名称":
                            bill.ShopName = value;
                            break;
                        case "门店编号":
                            bill.ShopNo = value;
                            break;
                        case "操作员":
                            bill.Operater = value;
                            break;
                        case "订单金额（元）":
                            bill.OrderAmount = decimal.Parse(value);
                            break;
                        case "商家实收（元）":
                            bill.PaySingleAmount = decimal.Parse(value);
                            break;
                        case "支付宝红包（元）":
                            bill.CouponAmount = decimal.Parse(value);
                            break;
                        case "集分宝（元）":
                            bill.AliJiFenAmount = decimal.Parse(value);
                            break;
                        case "支付宝优惠（元）":
                            bill.AliDiscountAmount = decimal.Parse(value);
                            break;
                        case "商家优惠（元）":
                            bill.MchDiscountAmount = decimal.Parse(value);
                            break;
                        case "券核销金额（元）":
                            bill.VolumeDiscountAmount = decimal.Parse(value);
                            break;
                        case "券名称":
                            bill.VolumeName = value;
                            break;
                        case "商家红包消费金额（元）":
                            bill.MchRedPackageAmount = decimal.Parse(value);
                            break;
                        case "卡消费金额（元）":
                            bill.CardAmount = decimal.Parse(value);
                            break;
                        case "商品名称":
                            bill.GoodsName = value;
                            break;
                        case "退款批次号/请求号":
                            bill.ApplyRefundNo = string.IsNullOrWhiteSpace(value) ? null : value;
                            break;
                        case "服务费（元）":
                            bill.HandlingFee = Math.Abs(decimal.Parse(value));
                            bill.FeeRate = bill.HandlingFee > 0 ? Convert.ToInt32(bill.HandlingFee /bill.PaySingleAmount * 1000) : 0;
                            bill.FeeRateStr = bill.HandlingFee > 0 ? bill.FeeRate / 10 + "%" : "0%";
                            break;
                        case "费率备注":
                            bill.FeeRateRemark = string.IsNullOrWhiteSpace(value) ? null : value;
                            break;
                        case "分润（元）":
                            bill.ActualReceiveAmount = Math.Abs(decimal.Parse(value)- bill.HandlingFee);
                            break;
                        case "备注":
                            bill.FeeRateRemark = value;
                            break;
                        default:
                            break;
                    }
                }
                bills.Add(bill);
            }
            return bills;
        }
        #endregion

        #region 支付宝对账单明细汇总提取
        /// <summary>
        /// 支付宝对账单明细汇总提取
        /// </summary>
        /// <param name="billStr">账单数据</param>
        /// <param name="tradeDate">交易日期</param>
        /// <param name="appType">应用类型</param>
        /// <param name="billType">账单类型</param>
        /// <returns></returns>
        private List<AliPayBillStatisticsEntity> GetBillStatistics(string billStr,DateTime tradeDate, SourceAppType appType,AliPayBillType billType)
        {
            billStr = billStr.Replace("\r", "");
            string[] data = billStr.Split(new [] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
            string[] billTitle = data[0].Split(',');

            List<AliPayBillStatisticsEntity> billStatisticss = new List<AliPayBillStatisticsEntity>();
            for (int i = 1; i < data.Length-1; i++)
            {
                string[] billResult = data[i].Split(',');
                AliPayBillStatisticsEntity bill = new AliPayBillStatisticsEntity();
                for (int j = 0; j < billTitle.Length; j++)
                {
                    string value = billResult[j].Trim();
                    switch (billTitle[j])
                    {
                        case "门店编号":
                            bill.TradeDate = tradeDate.Date;
                            bill.ShopNo = value;
                            break;
                        case "门店名称":
                            bill.ShopName = value;
                            bill.AppId = accountDic[appType.ToString()];
                            bill.AppName = appType.ToString();
                            bill.BusinessType = billType.ToString();
                            bill.BusinessTypeStr = billType.GetDescriptionString();
                            break;
                        case "交易订单总笔数":
                            bill.TradeOrderCount = Convert.ToInt32(value);
                            break;
                        case "退款订单总笔数":
                            bill.RefundOrderCount = Convert.ToInt32(value);
                            break;
                        case "订单金额（元）":
                            bill.OrderTotal = decimal.Parse(value);
                            break;
                        case "商家实收（元）":
                            bill.PayOrderTotal = decimal.Parse(value);
                            break;
                        case "支付宝优惠（元）":
                            bill.AliDiscountTotal = decimal.Parse(value);
                            break;
                        case "商家优惠（元）":
                            bill.MchDiscountTotal = decimal.Parse(value);
                            break;
                        case "卡消费金额（元）":
                            bill.CardTotal = decimal.Parse(value);
                            break;
                        case "服务费（元）":
                            bill.HandlingFeeTotal = Math.Abs(decimal.Parse(value));
                            break;
                        case "分润（元）":
                            bill.ActualReceiveTotal = Math.Abs(decimal.Parse(value)- bill.HandlingFeeTotal);
                            break;
                        case "实收净额（元）":
                            bill.PaidInNet = Math.Abs(decimal.Parse(value) - bill.HandlingFeeTotal);
                            break;
                        default:
                            break;
                    }
                }
                billStatisticss.Add(bill);
            }
            return billStatisticss;
        }
        #endregion

        #region 初始化支付宝应用账号信息字典
        private void InitAccountInfo()
        {
            accountDic.Add("ClientApp",configuration["Alipay:ClientApp:APP_ID"]);
            accountDic.Add("DriverApp", configuration["Alipay:DriverApp:APP_ID"]);
            accountDic.Add("PartnerApp",configuration["Alipay:PartnerApp:APP_ID"]);
        }
        #endregion

        #region 下载支付宝账单，并返回压缩文件地址
        /// <summary>
        /// 下载支付宝账单，并返回压缩文件地址
        /// </summary>
        /// <param name="billDate">账单日期</param>
        /// <param name="billType">账单类型</param>
        /// <param name="url">下载地址</param>
        /// <returns></returns>
        private Dictionary<string, string> DownloadBill(DateTime billDate, AliPayBillType billType, string url)
        {
            Dictionary<string, string> headers = new Dictionary<string, string>();
            HttpRequestDto httpRequestDto = new HttpRequestDto()
            {
                Headers = headers,
                Url = url,
                Method = MethodType.GET,
                ContentType = FormatType.JSON,
                Encoding = Encoding.UTF8.ToString(),
                ResponseType = ResponseHandleType.Download
            };
            HttpHandle http = new HttpHandle();
            HttpResponseDto response = http.HttpSendRequest(httpRequestDto);
            Dictionary<string, string> dic = url.Substring(url.IndexOf("?") + 1).Split('&').ToDictionary(p => p.Split('=')[0], v => v.Split('=')[1]);
            DirectoryInfo directoryInfo = new DirectoryInfo(Directory.GetCurrentDirectory());
            string path = Path.Combine(directoryInfo.FullName, "AliPayBillDownloadRecord", billDate.ToString("yyyy-MM-dd"));
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
            string filePath = Path.Combine(path, dic["downloadFileName"]).ToString();
            using (FileStream fs = new FileStream(filePath, FileMode.Create))
            {
                fs.Write(response.ResponseData, 0, response.ResponseData.Length);

                fs.Flush();
            }
            dic.Add("filePath", filePath);
            return dic;
        }

        #endregion
        #endregion
    }
}
